/*
    This file is part of Raptor.

    Raptor is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Raptor is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Raptor.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "xmlFileLoader.h"

#include <QUrl>
#include <QRegExp>
#include <QDir>
#include <QDebug>
#include <QTimer>
#include <QSettings>
#include <QDomDocument>

xmlFileLoader::xmlFileLoader()
{
        verbose = false;

	response = QByteArray();
	cookie = "";
	page = "";

	reinitLoop = false;
	waitForHttp = false;
	waitInsertAt = 0;
	waitHttp = QList<S_WAITFOR>();
	waitHttp.clear();

	waitingId = -1;

	m_downloaded = 0;
	m_fileSize = 0;
	m_progress = 0;

	m_state = STATE_LOAD_XML;
	m_error = ERROR_NO_ERROR;
	m_mode = MODE_FREE;

	step = 1;

	m_timeLeft = 0;
	wait = 0;
	timerStep = 1;

	continueCheck = true;

	regExpResults = QMap<QString, QString>();
	regExpListResults = QMap<QString, QStringList>();

	id = 0;
	m_logId = 0;
	maxConnectionsPerDownloads = 0;

	m_errorStr = "";

	url = QUrl("");
	host = "";

	m_downloadFile = new QFile();
	file = "";
	downloadDir = QDir::current();
	folder = "";

	m_downloadStart = QDateTime::currentDateTime();
	downloadEnd = QDateTime::currentDateTime();

	lastSize = 0;
	m_speed = 0;

	smooth = 20;
	speedIntervall = 500;
	speedList.clear();

	connect(&http, SIGNAL(requestFinished(int, bool)), this, SLOT(slot_http_requestFinished(int, bool)));
	connect(&http, SIGNAL(readyRead(const QHttpResponseHeader &)), this, SLOT(slot_http_readyRead(const QHttpResponseHeader &)));
	connect(&http, SIGNAL(dataReadProgress(int, int)), this, SLOT(slot_http_dataReadProgress(int, int)));
	connect(&http, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(slot_http_sslErrors(const QList<QSslError> &)));

	connect(&speedTimer, SIGNAL(timeout()), this, SLOT(slot_speedTimer_timeout()));
	connect(&waitTimer, SIGNAL(timeout()), this, SLOT(slot_waitTimer_timeout()));
}

xmlFileLoader::~xmlFileLoader()
{
	if(verbose) qDebug() << "~xmlFileLoader";

	if(m_downloadFile && m_downloadFile->isOpen()) m_downloadFile->close();

	delete m_downloadFile;

	m_state = STATE_DELETE;

	disconnect(&http, 0, this, 0);
	disconnect(&speedTimer, 0, this, 0);
	disconnect(&waitTimer, 0, this, 0);

	waitTimer.stop();
	speedTimer.stop();
	http.abort();
}

//http ->
void xmlFileLoader::slot_http_requestFinished(int requestId, bool httpError)
{
	if(verbose) qDebug() << "slot_http_requestFinished" << waitingId << requestId;

	//nur reagieren, wenn auch beabsichtigt
	if(waitingId != requestId) return;

	//wenn obj geloescht werden soll, nicht mehr reagieren..
	if(m_state == STATE_DELETE) return;

	if(httpError)
	{
		if(verbose) qDebug() << "slot_http_requestFinished, error:" << http.errorString();

		m_error = ERROR_HTTP;
		emit statusMsg(m_logId, "Error: " + http.errorString());
		emit downloadFinished(id, true);
	} else
	{
		page = QString(response);

		if(verbose) qDebug() << "Page:" << page;

		if(waitForHttp)
		{
			waitForHttp = false;

			for(int i=0; i<waitHttp.count(); ++i)
			{
				waitInsertAt = 1;
				if(verbose) qDebug() << "do step after wait:" << waitHttp.at(i).node.nodeName();
				doAction(waitHttp.at(i).node, waitHttp.at(i).it);
				waitHttp.removeFirst();
				--i;

				if(waitInsertAt != 1)
				{
					if(verbose) qDebug() << "should wait once again";
					break;
				}
			}

		} else
		{
			QDomNodeList tmpNodeList;

			switch(m_mode)
			{
				case MODE_FREE:
					tmpNodeList = domHoster.elementsByTagName("free");
					break;

				case MODE_PREMIUM:
					tmpNodeList = domHoster.elementsByTagName("premium");
					break;

				case MODE_LOGIN:
					tmpNodeList = domHoster.elementsByTagName("login");
					break;

				case MODE_GET_SIZE:
					tmpNodeList = domHoster.elementsByTagName("get_size");
					break;
			}

			if(tmpNodeList.at(0).namedItem("download_order").hasChildNodes())
			{
				QDomNode download_order = tmpNodeList.at(0).namedItem("download_order");
				QDomNode stepX = download_order.childNodes().at(step-1);

				if(stepX.hasChildNodes())
				{
					//alle moeglichen results durchgehen
					QDomNodeList resultList = stepX.namedItem("result").childNodes();

					for(int i=0; i<resultList.count(); ++i)
					{
						//wenn result zutrifft, fuehre alle actions aus
						if(switchResult(resultList.at(i)))
						{
							if(resultList.at(i).hasChildNodes())
							{
								doAction(resultList.at(i));
							}

							//nur ein result kann zutreffen
							break;
						}
					}
				} else
				{
					if(verbose) qDebug() << "step empty";
				}
			}
		}
	}
}

void xmlFileLoader::slot_http_readyRead(const QHttpResponseHeader & resp)
{
	Q_UNUSED(resp);
	response.append(http.readAll());
}

void xmlFileLoader::slot_http_dataReadProgress(int done, int total)
{
	if(m_state == STATE_DOWNLOADING)
	{
		m_downloaded = done;
		m_fileSize = total;

		if(m_fileSize > 0) m_progress = (int)((double)m_downloaded * 100.0 / (double)m_fileSize);
	}
}

void xmlFileLoader::slot_http_sslErrors(const QList<QSslError> &errors)
{
	if(verbose) qDebug() << "slot_http_sslErrors" << errors.size();
	http.ignoreSslErrors();
}
//<- http

//private ->

void xmlFileLoader::doAction(const QDomNode &parent, const int startI)
{
	if(waitForHttp)
	{
		if(verbose) qDebug() << "waiting for http";
		return;
	}

	if(verbose) qDebug() << "doAction, parent:" << parent.nodeName();

	int redoActionAt = 0;

	if(parent.hasChildNodes())
	{
		QDomNodeList actionList = parent.namedItem("action").childNodes();

		if(verbose) qDebug() << "doAction, count:" << actionList.count() << "start:" << startI;

		for(int i=startI; i<actionList.count(); ++i)
		{
			switchAction(actionList.at(i));

			if(reinitLoop)
			{
				if(verbose) qDebug() << "reinitLoop was set in:" << actionList.at(i).nodeName();
				redoActionAt = i+1;
				break;
			} else if(waitForHttp)
			{
				if(verbose) qDebug() << "new wait:" << waitInsertAt;
				waitHttp.insert(waitInsertAt, S_WAITFOR(parent, i+1));
				waitInsertAt++;
				break;
			}
		}
	}

	if(reinitLoop)
	{
		if(verbose) qDebug() << "reinitLoop doaction" << reinitLoop;

		reinitLoop = false;
		doAction(parent, redoActionAt);
	}
}

int xmlFileLoader::parseType(const QString &type)
{
	if(type == "open_url")
		return TYPE_OPEN_URL;
	else if(type ==  "get_size")
		return TYPE_GET_SIZE;
	else if(type == "get")
		return TYPE_METHOD_GET;
	else if(type == "get_download")
		return TYPE_METHOD_GET_DOWNLOAD;
	else if(type == "post")
		return TYPE_METHOD_POST;
	else if(type == "post_download")
		return TYPE_METHOD_POST_DOWNLOAD;
	else if(type == "regexp")
		return TYPE_REGEXP;
	else if (type == "multi_regexp")
		return TYPE_MULTI_REGEXP;
	else if(type == "step")
		return TYPE_STEP;
	else if(type == "do_step")
		return TYPE_DO_STEP;
	else if(type == "halt")
		return TYPE_HALT;
	else if(type == "halt_login")
		return TYPE_HALT_LOGIN;
	else if(type == "halt_get_size")
		return TYPE_HALT_GET_SIZE;
	else if(type == "wait")
		return TYPE_WAIT;
	else if(type == "prepare_download")
		return TYPE_PREPARE_DOWNLOAD;
	else if(type == "finish_download")
		return TYPE_FINISH_DOWNLOAD;
	else if(type == "check_file")
		return TYPE_CHECK_FILE;
	else if(type == "select_mirror")
		return TYPE_SELECT_MIRROR;
	else if(type == "check_size")
		return TYPE_CHECK_SIZE;
	else if(type == "check_content")
		return TYPE_CHECK_CONTENT;
	else if(type == "dummy")
		return TYPE_DUMMY;
	else if(type == "delete_file")
		return TYPE_DELETE_FILE;
	else if(type == "set_state")
		return TYPE_SET_STATE;
	else if(type == "break_check")
		return TYPE_BREAK_CHECK;
	else if(type == "set_cookie")
		return TYPE_SET_COOKIE;
	else if(type == "save_cookie")
		return TYPE_SAVE_COOKIE;
	else if(type == "load_settings")
		return TYPE_LOAD_SETTINGS;
	else if(type == "header")
		return TYPE_HEADER;
	else if(type == "wait_http")
		return TYPE_WAIT_HTTP;
	else if(type == "mode")
		return TYPE_MODE;
	else if(type == "bool_fct")
		return TYPE_BOOL_FCT;
	else if(type == "set_error")
		return TYPE_SET_ERROR;
	else if(type == "emit_state")
		return TYPE_EMIT_STATE;
	else
		return TYPE_NO_FOUND;
}

int xmlFileLoader::parseState(const QString &state)
{
	if(state == "running")
		return STATE_RUNNING;
	else if(state == "finished")
		return STATE_FINISHED;
	else if(state == "waiting")
		return STATE_WAITING;
	else if(state == "downloading")
		return STATE_DOWNLOADING;
	else
		return -1;
}

bool xmlFileLoader::switchResult(const QDomNode &result)
{
	if(verbose) qDebug() << "result" << result.nodeName() << result.attributes().namedItem("type").nodeValue();

	if(!checkAttributes(result, QStringList() << "type")) return false;

	switch(parseType(result.attributes().namedItem("type").nodeValue()))
	{
		default:

			emit statusMsg(m_logId, "Error: xml_result not found");
			return false;
			break;

		case TYPE_REGEXP:
		{
			if(!checkAttributes(result, QStringList() << "regexp")) return false;

			QRegExp tmpRegExp(result.attributes().namedItem("regexp").nodeValue());

			if(tmpRegExp.indexIn(page) != -1)
			{
				//wenn var vorhanden, alle vars in regExpResults speichern
				if(result.hasChildNodes())
				{
					QDomNamedNodeMap varList = result.namedItem("var").attributes();

					if(varList.count() == tmpRegExp.numCaptures())
					{
						for(int i=0; i<varList.count(); ++i)
						{
							addVar(varList.item(i).nodeName(), tmpRegExp.cap(varList.item(i).nodeValue().toInt()));
						}
					} else
					{
						if(verbose) qDebug() << "error: wrong var number";

						m_error = ERROR_XML;
						emit statusMsg(m_logId, "Error in xml file, to many var for regexp given");
						emit downloadFinished(id, true);
						return false;
					}
				}

				return true;
			} else
			{
				return false;
			}
			break;
		}

		case TYPE_CHECK_SIZE:
		{
			bool tmpReturn = true;

			if(!checkAttributes(result, QStringList() << "max_size")) return false;

			if(m_downloadFile && m_downloadFile->exists())
			{
				if(m_downloadFile->size() > result.attributes().namedItem("max_size").nodeValue().toInt())
					tmpReturn = false;
			} else
			{
				tmpReturn = false;
			}

			return tmpReturn;
			break;
		}

		case TYPE_CHECK_CONTENT:
		{
			if(!checkAttributes(result, QStringList() << "regexp")) return false;

			QRegExp tmpRegExp(result.attributes().namedItem("regexp").nodeValue());

			QString tmpStr = "";

			QFile tmpFile(downloadDir.filePath(file));
			if(tmpFile.exists())
			{
				if(!tmpFile.open(QIODevice::ReadOnly | QIODevice::Text))
					return false;

				tmpStr = tmpFile.readAll();
				tmpFile.close();

				if(tmpRegExp.indexIn(tmpStr) != -1) return true;

			} else
			{
				return false;
			}

			return false;
			break;
		}

		case TYPE_DUMMY:
		{
			//ueberpruefen, ob regexp gesetzt ist
			if(!result.attributes().contains("result"))
			{
				emit statusMsg(m_logId, "Warning in xml file, result not given");
				return true;
			} else
			{
				if(result.attributes().namedItem("result").nodeValue().toInt() == 1)
				{
					return true;
				} else
				{
					return false;
				}
			}

			break;
		}

		case TYPE_HEADER:
		{
			if(verbose) qDebug() << http.lastResponse().keys().join("|");

			if(!checkAttributes(result, QStringList() << "key" << "save")) break;

			if(http.lastResponse().hasKey(result.attributes().namedItem("key").nodeValue()))
			{
				addVar(result.attributes().namedItem("save").nodeValue(), http.lastResponse().value(result.attributes().namedItem("key").nodeValue()));

				return true;
			} else
			{
				return false;
			}

			break;
		}
	}

	return false;
}

void xmlFileLoader::switchAction(const QDomNode &action)
{
	if(verbose) qDebug() << "action" << action.nodeName() << action.attributes().namedItem("type").nodeValue();

	if(!checkAttributes(action, QStringList() << "type")) return;

	switch(parseType(action.attributes().namedItem("type").nodeValue()))
	{
		default:

			emit statusMsg(m_logId, "Error: xml, unknown action: " + QString("%1, %2").arg(step).arg(parseType(action.attributes().namedItem("type").nodeValue())) + " : " + action.nodeName() + " = " + action.attributes().namedItem("type").nodeValue());
			break;

		case TYPE_OPEN_URL:
		{
			QUrl tmpUrl;
			response = QByteArray();

			if(action.attributes().contains("url"))
			{
				QString tmpUrlStr = getVar(action.attributes().namedItem("url").nodeValue());
				tmpUrl = QUrl(tmpUrlStr);
			} else
			{
				tmpUrl = url;
			}

			if(!checkAttributes(action, QStringList() << "method")) break;

			//pruefen, welche methode gewahelt werden soll
			switch(parseType(action.attributes().namedItem("method").nodeValue()))
			{
				default:

					m_error = ERROR_XML;
					emit statusMsg(m_logId, "Error in xml: unknown method in open_url" + action.attributes().namedItem("method").nodeValue());
					emit downloadFinished(id, true);
					return;
					break;

				case TYPE_METHOD_GET:
				{
					if(verbose) qDebug() << "GET:" << tmpUrl.host() << tmpUrl.path();

					QHttpRequestHeader header("GET", tmpUrl.path());
					header.setValue("Host", tmpUrl.host());

					if(!cookie.isEmpty()) header.setValue("Cookie", cookie);

					if(tmpUrl.toString().left(5) == "https")
					{
						http.setHost(tmpUrl.host(), QHttp::ConnectionModeHttps);
					} else
					{
						http.setHost(tmpUrl.host());
					}

					//waitingId = http.get(tmpUrl.path());
					//break;
					waitingId = http.request(header, QByteArray());
					break;
				}

				case TYPE_METHOD_GET_DOWNLOAD:
				{
					if(verbose) qDebug() << "GET_DOWNLOAD:" << tmpUrl.host() << tmpUrl.path();

					emit statusMsg(m_logId, "Starting download.");
					speedTimer.start(speedIntervall);
					m_progress = 0;
					m_downloadStart = QDateTime::currentDateTime();

					QHttpRequestHeader header("GET", tmpUrl.path());
					header.setValue("Host", tmpUrl.host());

					if(!cookie.isEmpty()) header.setValue("Cookie", cookie);

					if(tmpUrl.toString().left(5) == "https")
					{
						http.setHost(tmpUrl.host(), QHttp::ConnectionModeHttps);
					} else
					{
						http.setHost(tmpUrl.host());
					}

					waitingId = http.request(header, QByteArray(), m_downloadFile);
					break;
				}

				case TYPE_METHOD_POST:
				{
					QByteArray post = "";

					QDomNamedNodeMap postData = action.namedItem("post_data").attributes();
					for(int i=0; i<postData.count(); ++i)
					{
						if(i > 0) post.append("&");
						post.append(postData.item(i).nodeName() + "=" + QUrl::toPercentEncoding(getVar(postData.item(i).nodeValue())));
					}

					QHttpRequestHeader header("POST", tmpUrl.path());
					header.setValue("Host", tmpUrl.host());

					//header.setValue("Content-Type", "application/x-www-form-urlencoded");
					QDomNamedNodeMap headerData = action.namedItem("header_data").attributes();
					for(int i=0; i<headerData.count(); ++i)
					{
						header.setValue(headerData.item(i).nodeName(), getVar(headerData.item(i).nodeValue()));
					}

					if(!cookie.isEmpty()) header.setValue("Cookie", cookie);

					if(verbose) qDebug() << "POST:" << tmpUrl.host() << tmpUrl.path() << "post:" << post << "header:" << header.keys().join("|");

					if(tmpUrl.toString().left(5) == "https")
					{
						http.setHost(tmpUrl.host(), QHttp::ConnectionModeHttps);
					} else
					{
						http.setHost(tmpUrl.host());
					}

					waitingId = http.request(header, post);

					break;
				}

				case TYPE_METHOD_POST_DOWNLOAD:
				{
					QByteArray post = "";

					QDomNamedNodeMap postData = action.namedItem("post_data").attributes();
					for(int i=0; i<postData.count(); ++i)
					{
						if(i > 0) post.append("&");
						post.append(postData.item(i).nodeName() + "=" + postData.item(i).nodeValue());
					}

					if(verbose) qDebug() << "POST_DOWNLOAD:" << tmpUrl.host() << tmpUrl.path() << post;

					emit statusMsg(m_logId, "Starting download.");
					speedTimer.start(speedIntervall);
					m_progress = 0;
					m_downloadStart = QDateTime::currentDateTime();

					QHttpRequestHeader header("POST", tmpUrl.path());
					header.setValue("Host", tmpUrl.host());

					if(!cookie.isEmpty()) header.setValue("Cookie", cookie);

					if(tmpUrl.toString().left(5) == "https")
					{
						http.setHost(tmpUrl.host(), QHttp::ConnectionModeHttps);
					} else
					{
						http.setHost(tmpUrl.host());
					}

					waitingId = http.request(header, post, m_downloadFile);
					break;
				}
			}

			break;
		}

		case TYPE_GET_SIZE:
		{
			if(!checkAttributes(action, QStringList() << "size" << "order")) break;

			QStringList sizeNames = QStringList() << " " << "K" << "M" << "G" << "T" << "P";
			for(int i=0; i<sizeNames.count(); ++i)
			{
				if(getVar(action.attributes().namedItem("order").nodeValue()) == sizeNames.at(i))
				{
					double tmpDbl = getVar(action.attributes().namedItem("size").nodeValue()).toDouble();

					double tmpMultipl = getVar("$multiplyer").toDouble();
					if(tmpMultipl <= 0) tmpMultipl = 1000;

					for(int o=0; o<i; ++o)
						tmpDbl = tmpDbl * tmpMultipl;

					m_fileSize = tmpDbl;
					return;
				}
			}

			break;
		}

		case TYPE_STEP:
		{
			if(!checkAttributes(action, QStringList() << "step")) break;

			step = action.attributes().namedItem("step").nodeValue().toInt();
			break;
		}

		case TYPE_DO_STEP:
		{
			if(!checkAttributes(action, QStringList() << "step")) break;

			step = action.attributes().namedItem("step").nodeValue().toInt();

			doStep();

			break;
		}

		case TYPE_HALT:
		{
			if(!checkAttributes(action, QStringList() << "error")) break;

			if(getVar(action.attributes().namedItem("error").nodeValue()).toInt() == 1)
			{
				m_state = STATE_ERROR;
				emit statusMsg(m_logId, QString("Error, in step: %1").arg(action.attributes().namedItem("step").nodeValue().toInt()));
				emit downloadFinished(id, true);
			} else
			{
				m_state = STATE_STOPPED;
				emit downloadFinished(id, false);
			}
			break;
		}

		case TYPE_HALT_LOGIN:
		{
			if(!checkAttributes(action, QStringList() << "error")) break;

			if(getVar(action.attributes().namedItem("error").nodeValue()).toInt() == 1)
			{
				emit loginFinished(id, true);
				m_state = STATE_ERROR;
				emit statusMsg(m_logId, QString("Error, in step: %1").arg(action.attributes().namedItem("step").nodeValue().toInt()));
				emit downloadFinished(id, true);
			} else
			{
				emit loginFinished(id, false);
				m_state = STATE_STOPPED;
				emit downloadFinished(id, false);
			}
			break;
		}

		case TYPE_HALT_GET_SIZE:
		{
			if(!checkAttributes(action, QStringList() << "error")) break;

			if(getVar(action.attributes().namedItem("error").nodeValue()).toInt() == 1)
			{
				emit getSizeFinished(id, true);
				m_state = STATE_ERROR;
				emit statusMsg(m_logId, QString("Error, in step: %1").arg(action.attributes().namedItem("step").nodeValue().toInt()));
				emit downloadFinished(id, true);
			} else
			{
				emit getSizeFinished(id, false);
				m_state = STATE_STOPPED;
				emit downloadFinished(id, false);
			}
			break;
		}

		case TYPE_WAIT:
		{
			if(!checkAttributes(action, QStringList() << "wait" << "multiplyer" << "step")) break;

			int tmpWait = getVar(action.attributes().namedItem("wait").nodeValue()).toInt();

			int tmpMultipl = action.attributes().namedItem("multiplyer").nodeValue().toInt();
			int tmpStep = action.attributes().namedItem("step").nodeValue().toInt();

			m_timeLeft = tmpWait*tmpMultipl;
			wait = m_timeLeft;
			timerStep = tmpStep;

			waitTimer.start(1000);

			//QTimer::singleShot(1000, this, SLOT(slot_waitTimer_singleShot()));

			break;
		}

		case TYPE_REGEXP:
		{
			if(!checkAttributes(action, QStringList() << "regexp")) return;

			QRegExp tmpRegExp(action.attributes().namedItem("regexp").nodeValue());

			if(tmpRegExp.indexIn(page) != -1)
			{
				//wenn var vorhanden, alle vars in regExpResults speichern
				if(action.hasChildNodes())
				{
					QDomNamedNodeMap varList = action.namedItem("var").attributes();

					if(varList.count() == tmpRegExp.numCaptures())
					{
						for(int i=0; i<varList.count(); ++i)
						{
							addVar(varList.item(i).nodeName(), tmpRegExp.cap(varList.item(i).nodeValue().toInt()));
						}
					} else
					{
						if(verbose) qDebug() << "error: wrong var number";

						m_error = ERROR_XML;
						emit statusMsg(m_logId, "Error in xml file, to many var for regexp given");
						emit downloadFinished(id, true);
						return;
					}
				}
			}
			break;
		}

		case TYPE_MULTI_REGEXP:
		{
			if(!checkAttributes(action, QStringList() << "regexp")) break;

			QRegExp tmpRegExp(action.attributes().namedItem("regexp").nodeValue());

			if(tmpRegExp.indexIn(page) != -1)
			{
				if(action.hasChildNodes())
				{
					QStringList tmpStrList;
					tmpRegExp.setMinimal(true);
					int pos = 0;

					QDomNamedNodeMap varList = action.namedItem("var").attributes();

					QList<QStringList> tmpList;

					while((pos = tmpRegExp.indexIn(page, pos)) != -1)
					{
						QStringList tmpCapStrList;
						for(int i=0; i<varList.count(); ++i)
						{
							tmpCapStrList.append(tmpRegExp.cap(varList.item(i).nodeValue().toInt()));
						}

						tmpList.append(tmpCapStrList);

						pos += tmpRegExp.matchedLength();
					}

					for(int i=0; i<varList.count(); ++i)
					{
						QStringList tmpVarStrList;
						for(int o=0; o<tmpList.count(); ++o)
						{
							tmpVarStrList.append(tmpList.at(o).at(i));
						}

						regExpListResults.insert(varList.item(i).nodeName(), tmpVarStrList);
						if(verbose) qDebug() << "regExpListResults:" << varList.item(i).nodeName() << tmpVarStrList.join("|");
					}
				}
			}

			break;
		}

		case TYPE_PREPARE_DOWNLOAD:
		{
			if(!downloadDir.exists(folder))
			{
				downloadDir.mkdir(folder);
			}

			downloadDir.cd(folder);

			while(downloadDir.exists(file))
			{
				emit statusMsg(m_logId, "file exists, renaming.");
				file = "_" + file;
			}

			m_downloadFile = new QFile(downloadDir.filePath(file));
			if(!m_downloadFile->open(QIODevice::WriteOnly))
			{
				m_error = ERROR_FILE;
				emit statusMsg(m_logId, "Cant open file.");
				emit downloadFinished(id, true);
				return;
			}

			break;
		}

		case TYPE_SELECT_MIRROR:
		{
			if(!checkAttributes(action, QStringList() << "list" << "use")) break;

			QStringList tmpListStr = getListVar(action.attributes().namedItem("list").nodeValue());

			addVar(action.attributes().namedItem("use").nodeValue(), tmpListStr.at(0));

			break;
		}

		case TYPE_FINISH_DOWNLOAD:
		{
			m_downloadFile->close();
			speedTimer.stop();
			downloadEnd = QDateTime::currentDateTime();

			emit statusMsg(m_logId, "Download finished.");
			break;
		}

		case TYPE_BREAK_CHECK:
		{
			continueCheck = false;
			break;
		}

		case TYPE_CHECK_FILE:
		{
			continueCheck = true;

			QDomNodeList checks = action.namedItem("check").childNodes();
			for(int i=0; i<checks.count(); ++i)
			{
				//checks koennen unterbrochen werden durch action=break_check
				if(!continueCheck) break;

				//checks sind results aber haben andere reihenfolge
				//wenn result zutrifft, fuehre actions in true aus, sonst die in false
				//wenn weder true noch false gegeben, brauchst erst gar nich guggen
				if(checks.at(i).hasChildNodes())
				{
					if(switchResult(checks.at(i)))
					{
						if(checks.at(i).namedItem("true").hasChildNodes())
						{
							if(verbose) qDebug() << "checkfile doaction";
							doAction(checks.at(i).namedItem("true"));
						}
					} else
					{
						if(checks.at(i).namedItem("false").hasChildNodes())
						{
							if(verbose) qDebug() << "checkfile doaction";
							doAction(checks.at(i).namedItem("false"));
						}
					}
				}
			}

			break;
		}

		case TYPE_DELETE_FILE:
		{
			if(m_downloadFile->exists())
			{
				m_downloadFile->remove();
			}

			break;
		}

		case TYPE_SET_STATE:
		{
			if(!checkAttributes(action, QStringList() << "state")) break;
			//emit stateChanged();

			int tmpState = parseState(action.attributes().namedItem("state").nodeValue());
			if(tmpState >= 0)
			{
				m_state = E_STATE(tmpState);
			} else
			{
				m_error = ERROR_XML;
				emit statusMsg(m_logId, "Error in xml file, state not valid");
				emit downloadFinished(id, true);
				return;
			}

			break;
		}

		case TYPE_LOAD_SETTINGS:
		{
			if(action.hasChildNodes())
			{
				QDomNamedNodeMap varList = action.namedItem("var").attributes();

				QSettings settings("raptor-loader", "Raptor");

				for(int i=0; i<varList.count(); ++i)
				{
					addVar(varList.item(i).nodeValue(), settings.value("hoster/" + host + "/settings/" + varList.item(i).nodeName(), "not found").toString());
				}
			}
			break;
		}

		case TYPE_SAVE_COOKIE:
		{
			if(!checkAttributes(action, QStringList() << "cookie")) break;

			QStringList cookieParams = QStringList() << "Comment" << "Domain" << "Max-Age" << "Path" << "Secure" << "Version" << "Expires";

			QSettings settings("raptor-loader", "Raptor");

			QStringList tmpList = getVar(action.attributes().namedItem("cookie").nodeValue()).split(";");

			for(int i=0; i<tmpList.size(); ++i)
			{
				QStringList tmpList2 = tmpList.at(i).split("=");

				if(tmpList2.size() == 2 && !cookieParams.contains(tmpList2.at(0).trimmed(), Qt::CaseInsensitive)) settings.setValue("hoster/" + host + "/settings/" + tmpList2.at(0).trimmed(), tmpList2.at(1).trimmed());
			}

			break;
		}

		case TYPE_SET_COOKIE:
		{
			QStringList tmpCookie = QStringList();

			if(action.hasChildNodes())
			{
				QDomNamedNodeMap varList = action.namedItem("var").attributes();

				for(int i=0; i<varList.count(); ++i)
				{
					tmpCookie.append(varList.item(i).nodeName() + "=" + getVar(varList.item(i).nodeValue()));
				}
			}

			cookie = tmpCookie.join("&");

			if(verbose) qDebug() << "cookie:" << cookie;
			break;
		}

		case TYPE_HEADER:
		{
			if(!checkAttributes(action, QStringList() << "key" << "save")) break;

			qDebug() << "header:" << http.lastResponse().keys().join("|");

			if(http.lastResponse().hasKey(action.attributes().namedItem("key").nodeValue()))
			{
				if(verbose) qDebug() << "header:" << http.lastResponse().value(action.attributes().namedItem("key").nodeValue());
				addVar(action.attributes().namedItem("save").nodeValue(), http.lastResponse().value(action.attributes().namedItem("key").nodeValue()));
			} else
			{
				m_error = ERROR_XML;
				emit statusMsg(m_logId, "Error in xml file, key not in header");
				emit downloadFinished(id, true);
				return;
			}

			break;
		}

		case TYPE_WAIT_HTTP:
		{
			waitForHttp = true;
			break;
		}

		case TYPE_MODE:
		{
			if(!checkAttributes(action, QStringList() << "mode")) break;

			if(action.attributes().namedItem("mode").nodeValue() == "premium")
				emit changeMode(id, MODE_PREMIUM);
			else
				emit changeMode(id, MODE_FREE);

			break;
		}

		case TYPE_BOOL_FCT:
		{
			if(!checkAttributes(action, QStringList() << "function" << "save")) break;

			bool tmpBool = true;

			if(action.attributes().namedItem("function").nodeValue() == "isEmpty")
			{
				if(!checkAttributes(action, QStringList() << "input1")) break;

				tmpBool = getVar(action.attributes().namedItem("input1").nodeValue()).isEmpty();

				addVar(action.attributes().namedItem("save").nodeValue(), tmpBool ? "1": "0");
			}

			break;
		}

		case TYPE_SET_ERROR:
		{
			if(!checkAttributes(action, QStringList() << "error")) break;

			m_errorStr = getVar(action.attributes().namedItem("error").nodeValue());
			break;
		}

		case TYPE_EMIT_STATE:
		{
			if(!checkAttributes(action, QStringList() << "msg")) break;

			emit statusMsg(m_logId, getVar(action.attributes().namedItem("msg").nodeValue()));
			break;
		}
	}
}

void xmlFileLoader::addVar(const QString &key, const QString &value)
{
	regExpResults.insert(key, value);
	if(verbose) qDebug() << "addVar:" << key << value;
}

QString xmlFileLoader::getVar(const QString &var)
{
	if(verbose) qDebug() << "getVar:" << var;

	if(var == "$URL") return url.toString();

	QString tmpStr = var;

	if(tmpStr.left(1) == "$")
	{
		if(regExpResults.contains(tmpStr.mid(1)))
		{
			tmpStr = regExpResults.value(tmpStr.mid(1));
		} else
		{
			if(verbose) qDebug() << "var" << var << "doesnt exist";
			m_error = ERROR_XML;
			emit statusMsg(m_logId, "Error in xml file, var not present");
			emit downloadFinished(id, true);
			return "";
		}
	}

	return tmpStr;
}

QStringList xmlFileLoader::getListVar(const QString &var)
{
	if(verbose) qDebug() << "getListVar:" << var;

	QStringList tmpStrList;

	if(var.left(1) == "$")
	{
		if(regExpListResults.contains(var.mid(1)))
		{
			tmpStrList = regExpListResults.value(var.mid(1));
		} else
		{
			if(verbose) qDebug() << "var" << var << "doesnt exist";

			m_error = ERROR_XML;
			emit statusMsg(m_logId, "Error in xml file, var not present");
			emit downloadFinished(id, true);

			return QStringList();
		}
	} else
	{
		tmpStrList = var.split("|");
	}

	return tmpStrList;
}

bool xmlFileLoader::checkAttributes(const QDomNode &node, const QStringList &attributes)
{
	for(int i=0; i<attributes.count(); ++i)
	{
		if(!node.attributes().contains(attributes.at(i)))
		{
			if(verbose) qDebug() << "checkAttributes: found Error";

			m_state = STATE_ERROR;
			m_error = ERROR_XML;
			emit statusMsg(m_logId, "Error in xml file, attribute" + attributes.at(i) + " not present");
			emit downloadFinished(id, true);
			return false;
		}
	}

	return true;
}

void xmlFileLoader::doStep()
{
	QDomNodeList tmpNodeList;

	switch(m_mode)
	{
		case MODE_FREE:
			tmpNodeList = domHoster.elementsByTagName("free");
			break;

		case MODE_PREMIUM:
			tmpNodeList = domHoster.elementsByTagName("premium");
			break;

		case MODE_LOGIN:
			tmpNodeList = domHoster.elementsByTagName("login");
			break;

		case MODE_GET_SIZE:
			tmpNodeList = domHoster.elementsByTagName("get_size");
			break;
	}

	if(tmpNodeList.at(0).namedItem("download_order").hasChildNodes())
	{
		QDomNode download_order = tmpNodeList.at(0).namedItem("download_order");
		QDomNode stepX = download_order.childNodes().at(step-1);

		if(stepX.hasChildNodes())
		{
			doAction(stepX);
		}
	}
}

void xmlFileLoader::loadVar()
{
	QDomNodeList tmpNodeList = domHoster.elementsByTagName("var");

	if(tmpNodeList.at(0).hasAttributes())
	{
		for(int i=0; i<tmpNodeList.at(0).attributes().size(); ++i)
		{
			addVar(tmpNodeList.at(0).attributes().item(i).nodeName(), tmpNodeList.at(0).attributes().item(i).nodeValue());
		}
	}
}

bool xmlFileLoader::loadXmlFile(const QString &xmlFile)
{
	QFile file(xmlFile);
	domHoster = QDomDocument("hoster");

	if(!file.open(QIODevice::ReadOnly)) return false;

	if(!domHoster.setContent(&file))
	{
		file.close();
		return false;
	}

	m_state = STATE_STOPPED;
	loadVar();
	return true;
}

void xmlFileLoader::setSpeed(const double &newSpeed)
{
	if(!speedList.isEmpty() && speedList.count() == smooth)
	{
		speedList.removeFirst();
		speedList.append(newSpeed);
	} else
	{
		speedList.append(newSpeed);
	}

	double tmpDbl = 0;

	for(int i=0; i<speedList.size(); ++i)
	{
		tmpDbl += speedList.at(i);
	}

	if(speedList.isEmpty())
		m_speed = 0;
	else
		m_speed = (tmpDbl / speedList.size());

	if(m_speed < 1) m_speed = 0;
}
//<- private

//public ->
bool xmlFileLoader::isValidHoster(const QString &host)
{
	QDomNodeList tmpNodeList = domHoster.elementsByTagName("url_regexp");

	if(!tmpNodeList.isEmpty())
	{
		if(tmpNodeList.at(0).attributes().contains("host"))
		{
			if(host.indexOf(QRegExp(tmpNodeList.at(0).attributes().namedItem("host").nodeValue())) != -1)
			{
				return true;
			} else
			{
				return false;
			}
		}
	}

	return false;
}

bool xmlFileLoader::setLink(const QUrl &newUrl)
{
	if(isValidHoster(newUrl.host()))
	{
		bool tmpBool = true;

		if(domHoster.childNodes().at(0).attributes().contains("host"))
			host = domHoster.childNodes().at(0).attributes().namedItem("host").nodeValue();
		else
			host = newUrl.host();

		if(domHoster.childNodes().at(0).attributes().contains("allowOtherHostnames"))
			tmpBool = domHoster.childNodes().at(0).attributes().namedItem("allowOtherHostnames").nodeValue() == "0" ? false: true;

		url = newUrl;
		if(!tmpBool) url.setHost(host);

		return true;
	} else
	{
		return false;
	}
}

void xmlFileLoader::setFolder(const QString &newFolder)
{
	folder = newFolder;
}

void xmlFileLoader::setDownloadDir(const QString &newFolder)
{
	downloadDir = QDir(newFolder);
}

void xmlFileLoader::setFile(const QString &newFile)
{
	file = newFile;
}

void xmlFileLoader::setLogId(const int &newId)
{
	m_logId = newId;
}

void xmlFileLoader::setId(const int &newId)
{
	id = newId;
}

void xmlFileLoader::setMode(const E_MODE &newMode)
{
	m_mode = newMode;
}

void xmlFileLoader::setProxy(QNetworkProxy const &proxy)
{
    http.setProxy(proxy);
}

void xmlFileLoader::start()
{
	if(verbose) qDebug() << "START" << reinitLoop;

	m_downloadStart = QDateTime::currentDateTime();
	downloadEnd = QDateTime::currentDateTime();

	doStep();
}

void xmlFileLoader::loadXml(const QString &xmlFile)
{
	if(!loadXmlFile(xmlFile))
	{
		if(verbose) qDebug() << "error in xml file";

		m_error = ERROR_XML;
		m_state = STATE_ERROR;

		emit downloadFinished(id, true);
	}
}

int xmlFileLoader::state()
{
	return m_state;
}

int xmlFileLoader::error()
{
	return m_error;
}

int xmlFileLoader::progress()
{
	return m_progress;
}

double xmlFileLoader::speed()
{
	return m_speed;
}

int xmlFileLoader::timeLeft()
{
	return m_timeLeft;
}

int xmlFileLoader::logId()
{
	return m_logId;
}

QPointer<QFile> xmlFileLoader::downloadFile()
{
    return m_downloadFile;
}

QString xmlFileLoader::link()
{
	return url.toString();
}

QDateTime xmlFileLoader::downloadStart()
{
	return m_downloadStart;
}

uint xmlFileLoader::fileSize()
{
	return m_fileSize;
}

int xmlFileLoader::mode()
{
	return m_mode;
}

uint xmlFileLoader::downloaded()
{
	return m_downloaded;
}

void xmlFileLoader::stop()
{
	if(m_state != STATE_STOPPED)
	{
		if(m_downloadFile->isOpen()) m_downloadFile->close();
		http.abort();
	}

	m_state = STATE_STOPPED;
}

void xmlFileLoader::login(const QString &user, const QString &pass)
{
	setMode(MODE_LOGIN);

	addVar("username", user);
	addVar("password", pass);

	doStep();
}

void xmlFileLoader::getSize()
{
	setMode(MODE_GET_SIZE);

	doStep();
}

QString xmlFileLoader::errorStr()
{
	return m_errorStr;
}
//<-public

//slots ->
void xmlFileLoader::slot_waitTimer_timeout()
{
	if(m_state == STATE_STOPPED) return;

	--m_timeLeft;

	if(wait > 0) m_progress = (int)(100 * ((double)(wait - m_timeLeft) / wait));

	if(m_timeLeft <= 0)
	{
		waitTimer.stop();

		step = timerStep;
		doStep();
	}
}
//<- slots

//speedTimer
void xmlFileLoader::slot_speedTimer_timeout()
{
	if(speedTimer.interval() > 0) setSpeed((m_downloaded - lastSize) / ((double)speedTimer.interval() / 1000.0));

	lastSize = m_downloaded;

	if(m_speed > 0) m_timeLeft = (int)((double)(m_fileSize - m_downloaded) / m_speed);
}
//<- speedTimer
